import argparse
import os
import re
import subprocess
import glob
import multiprocessing
from datetime import datetime
from multiprocessing import Pool
import sys
import tqdm
from natsort import natsorted

# 全局变量控制详细输出
verbose = False

def log_verbose(message):
    """打印详细日志信息"""
    if verbose:
        print(f"[VERBOSE] {message}")

def get_timestamp():
    """获取当前时间戳，格式为%Y.%m.%d_%H.%M.%S"""
    return datetime.now().strftime("%Y.%m.%d_%H.%M.%S")

def find_files(input_paths, regex_pattern, file_filter='all', recursive=False):
    """根据输入路径、正则表达式和文件类型查找文件"""
    files = []
    pattern = re.compile(regex_pattern)

    log_verbose(f"开始查找文件，正则表达式: {regex_pattern}, 文件类型: {file_filter}, 递归模式: {recursive}")

    for input_path in input_paths:
        log_verbose(f"处理输入路径: {input_path}")

        if os.path.isfile(input_path):
            # 如果是单个文件
            log_verbose(f"检测到单个文件: {input_path}")
            # 根据文件类型过滤
            if file_filter == 'dir':
                log_verbose(f"跳过文件（仅处理目录模式）: {input_path}")
                continue

            filename = os.path.abspath(input_path)
            files.append(filename)
            log_verbose(f"添加文件: {filename}")
        elif os.path.isdir(input_path):
            # 如果是目录
            log_verbose(f"检测到目录: {input_path}")
            
            if recursive:
                # 递归遍历目录
                log_verbose(f"开始递归遍历目录: {input_path}")
                files.append(os.path.abspath(input_path))
                for root, dirs, filenames in os.walk(input_path):
                    log_verbose(f"遍历目录: {root}")
                    
                    # 处理目录
                    if file_filter in ['dir', 'all']:
                        for dirname in dirs:
                            full_dir_path = os.path.abspath(os.path.join(root, dirname))
                            files.append(full_dir_path)
                    
                    # 处理文件
                    if file_filter in ['file', 'all']:
                        for filename in filenames:
                            full_file_path = os.path.abspath(os.path.join(root, filename))
                            files.append(full_file_path)
            else:
                # 非递归模式，只遍历当前目录
                files.append(os.path.abspath(input_path))
                log_verbose(f"添加目录: {os.path.abspath(input_path)}")
                for f in os.listdir(input_path):
                    filename = os.path.abspath(os.path.join(input_path, f))
                    if file_filter == 'dir' and os.path.isdir(filename):
                        files.append(filename)
                        log_verbose(f"添加目录: {filename}")
                    elif file_filter == 'file' and os.path.isfile(filename):
                        files.append(filename)
                        log_verbose(f"添加文件: {filename}")
                    elif file_filter == 'all':
                        files.append(filename)
                        log_verbose(f"添加文件/目录: {filename}")
    
    log_verbose(f"总共找到 {len(files)} 个文件")
    # 使用正则表达式匹配文件路径
    files = [path for path in files if pattern.search(path)]
    log_verbose(f"匹配文件数量: {len(files)}")
    return files

def read_template_file(template_path):
    """读取模板文件内容"""
    try:
        log_verbose(f"开始读取模板文件: {template_path}")
        
        if not os.path.exists(template_path):
            print(f"错误: 模板文件不存在: {template_path}")
            return None
            
        if not os.path.isfile(template_path):
            print(f"错误: 指定的路径不是文件: {template_path}")
            return None
            
        with open(template_path, 'r', encoding='utf-8') as f:
            content = f.read().strip()
            
        log_verbose(f"成功读取模板文件，内容长度: {len(content)} 字符")
        return content
        
    except Exception as e:
        print(f"错误: 读取模板文件时发生异常: {template_path}")
        print(f"异常信息: {str(e)}")
        return None

def generate_command(script, filepath, regex):
    """根据模板生成命令"""
    log_verbose(f"开始生成命令，模板: {script}，文件：{filepath}，正则：{regex}")

    command = script
    original_command = command

    # 替换${path}变量
    if '${path}' in command:
        filepath =  re.sub(r'\'|\"', '', filepath)
        command = command.replace('${path}', filepath)
        log_verbose("替换 ${path} -> " + filepath)
    # 替换${parent}变量，支持索引 ${parent[2]}、${parent[-1]} 等
    if '${parent' in command:
        # 预处理：去掉 filepath 两侧引号
        clean_filepath = re.sub(r"[\'\"]", "", filepath)
        abs_path = os.path.abspath(clean_filepath)

        def resolve_parent(base_path, idx):
            # idx 约定：-1 表示当前父目录，-2 表示再上一层，以此类推
            # 0 或 1 风格都常见，这里统一为：
            # - 无索引或 [-1] 等价于 os.path.dirname(base_path)
            # - 非负整数 n：向上 n 层（0 表示当前父目录，1 表示再上一层，以此类推）
            # - 负整数 -k：等价于向上 (k-1) 层（-1 -> 0 层，-2 -> 1 层 ...）
            parent_path = os.path.abspath(base_path)

            def up(p):
                return os.path.dirname(p) if p != os.path.dirname(p) else p

            if idx is None:
                steps = 0
            else:
                if idx < 0:
                    steps = (-idx) - 1
                else:
                    steps = idx

            for _ in range(steps + 1):
                parent_path = up(parent_path)

            return parent_path

        # 查找所有 ${parent} 或 ${parent[...]} 占位符
        pattern_parent = re.compile(r"\$\{parent(?:\[(\-?\d+)\])?\}")
        matches = list(pattern_parent.finditer(command))

        for m in matches:
            idx_str = m.group(1)
            idx = int(idx_str) if idx_str is not None else None
            parent_value = resolve_parent(abs_path, idx)
            command = command.replace(m.group(0), parent_value)
            log_verbose(f"替换 {m.group(0)} -> {parent_value}")

    # 替换${time}变量
    if '${time}' in command:
        timestamp = get_timestamp()
        command = command.replace('${time}', timestamp)
        log_verbose("替换 ${time} -> " + timestamp)

    # 替换${filename}变量（不含路径的文件名）
    if '${filename}' in command:
        filename = os.path.basename(filepath)
        command = command.replace('${filename}', filename)
        log_verbose("替换 ${filename} -> " + filename)

    # 替换${dirname}变量（目录路径）
    if '${dirname}' in command:
        dirname = os.path.dirname(filepath)
        command = command.replace('${dirname}', dirname)
        log_verbose("替换 ${dirname} -> " + dirname)

    # 替换${name}变量（不含扩展名的文件名）
    if '${name}' in command:
        name = os.path.splitext(os.path.basename(filepath))[0]
        command = command.replace('${name}', name)
        log_verbose("替换 ${name} -> " + name)

    # 替换${ext}变量（文件扩展名）
    if '${ext}' in command:
        ext = os.path.splitext(filepath)[1]
        command = command.replace('${ext}', ext)
        log_verbose("替换 ${ext} -> " + ext)

    # 替换${1}-${10}正则匹配组
    regex_result = re.search(regex, filepath)
    if regex_result:
        for i, group in enumerate(regex_result.groups(), 1):
            placeholder = f'${{{i}}}'
            if placeholder in command:
                try:
                    group_value = regex_result.group(i) if regex_result.group(i) else ''
                    command = command.replace(placeholder, group_value)
                    log_verbose(f"替换 ${{{i}}} -> '{group_value}'")
                except (IndexError, AttributeError):
                    command = command.replace(placeholder, '')
                    log_verbose(f"替换 ${{{i}}} -> '' (组不存在)")

    log_verbose(f"命令生成完成: {original_command} -> {command}")
    return command

def execute_command(command):
    """执行单个命令"""
    try:
        print(f"command: {command}")
        result = subprocess.run(command, shell=True, capture_output=True, text=True)

        log_verbose(f"命令执行完成，返回码: {result.returncode}")
        log_verbose(f"标准输出长度: {len(result.stdout) if result.stdout else 0} 字符")
        log_verbose(f"标准错误长度: {len(result.stderr) if result.stderr else 0} 字符")

        if result.returncode == 0:
            # 始终打印标准输出，不仅仅在verbose模式下
            if result.stdout:
                print(result.stdout.rstrip())  # 使用rstrip()去除末尾换行符
        else:
            print(f"execute failed command: {command}")
            if result.stderr:
                print(f"错误: {result.stderr}")

        return result.returncode == 0

    except Exception as e:
        print(f"✗ 执行命令时发生异常: {command}")
        print(f"异常信息: {str(e)}")
        return False

def main():
    global verbose

    # 创建argparse解析器
    parser = argparse.ArgumentParser(
        description="批量命令生成和执行工具",
        formatter_class=argparse.RawDescriptionHelpFormatter,
    epilog="""
示例用法:
# 转换文件格式
python batch_command.py input_path -r "(.*)\\.yuv$" -s "ffmpeg -i ${path} ${1}.mp4" --sype file

# 使用时间戳备份
python batch_command.py input_path -r ".*\\.txt$" -s "cp ${path} ${dirname}/${name}_${time}${ext}" --sype file

# 批量重命名
python batch_command.py input_path -r "old_(.*)\\.jpg$" -s "mv ${path} ${dirname}/new_${1}.jpg" --sype file -e

# 使用模板文件
python batch_command.py input_path -r ".*\\.jpg$" -t template.txt --sype file -e
"""
    )

    # 添加命令行参数
    parser.add_argument('input',
                       nargs='+',
                       help='输入文件路径列表，支持文件、目录或通配符')

    parser.add_argument('-r', '--regex',
                       default=".*",
                       help='正则表达式过滤器，用于匹配文件名或目录名')

    parser.add_argument('-s', '--script',
                   type=str,
                   help='''执行脚本，其中的命令模板支持变量: ${1}-${10}(正则组), \n
                   ${path}(完整路径), \n
                   ${parent}(父目录), \n
                   ${filename}(文件名),\n
                     ${dirname}(目录), \n
                     ${name}(无扩展名), \n
                     ${ext}(扩展名), \n
                     ${time}(时间戳)\n
                     为空时仅显示过滤结果''')


    parser.add_argument('-t', '--template',
                   type=str,
                   help='''命令模板文件路径，和script作用一样，读取模版文件执行脚本，支持变量: ${1}-${10}(正则组), \n
                   ${path}(完整路径), \n
                   ${parent}(父目录), \n
                   ${filename}(文件名),\n
                     ${dirname}(目录), \n
                     ${name}(无扩展名), \n
                     ${ext}(扩展名), \n
                     ${time}(时间戳)\n''')

    parser.add_argument('-f', '--file_filter',
                       choices=['file', 'dir', 'all'],
                       default='all',
                       help='处理的文件类型: file(仅文件), dir(仅目录), all(文件和目录), 默认为all')

    parser.add_argument('-R', '--recursive',
                       action='store_true',
                       help='递归遍历子目录，默认只遍历当前目录')

    parser.add_argument('-e', '--execute',
                       action='store_true',
                       help='生成命令后是否执行，默认不执行')

    parser.add_argument('-j', '--join',
                       type=str,
                       help='当join不为空时把遍历的列表用分隔符连接')
    
    parser.add_argument('-m', '--multi-process',
                       action='store_true',
                       help='是否使用多进程执行命令')
    
    parser.add_argument('-p', '--progress',
                    action='store_true',
                    help='显示命令执行进度条（仅在单进程模式下有效）')


    parser.add_argument('--not_sorted', action='store_true', 
                        help='不要对过滤文件进行自动排序')

    parser.add_argument('--max-workers',
                       type=int,
                       default=multiprocessing.cpu_count(),
                       help='多进程执行时的最大工作进程数，默认为CPU核心数')

    
    parser.add_argument('-v', '--verbose',
                       action='store_true',
                       help='打印详细的执行日志，包括文件遍历、正则匹配和命令执行过程')

    # 解析参数
    args = parser.parse_args()
    # 设置全局verbose标志
    verbose = args.verbose
    if verbose:
        log_verbose("启用详细日志模式")
        log_verbose(f"解析的参数: {vars(args)}")

    log_verbose("=== 批量命令生成工具 ===")
    log_verbose(f"输入路径: {args.input}")
    log_verbose(f"正则表达式: {args.regex}")
    log_verbose(f"文件过滤类型: {args.file_filter}")
    log_verbose(f"递归模式: {'是' if args.recursive else '否'}")
    if args.script:
        log_verbose(f"命令模板模式: {args.script}")
    elif args.template:
        log_verbose(f"模板文件模式: {args.template}")
    log_verbose(f"执行模式: {'执行' if args.execute else '仅生成'}")
    if args.execute:
        log_verbose(f"进程模式: {'多进程' if args.multi_process else '单进程'}")
    
    log_verbose("开始文件查找")
    # 正则匹配的文件
    matched_files = find_files(args.input, args.regex, args.file_filter, args.recursive)
    if args.not_sorted:
        log_verbose("不自动排序匹配文件列表")
    else:
        log_verbose("自动排序匹配文件列表")
        matched_files = natsorted(matched_files)
    print(f'matched files({len(matched_files)}):')
    for path in matched_files:
        print(path)
    commands = []
    script_template_content = None
    # 生成命令
    log_verbose("开始命令生成:")
    if args.script:
        log_verbose(f"命令模板: { args.script}")
        script_template_content = args.script
    elif args.template:
        log_verbose(f"模板文件: {args.template}")
        if os.path.isfile(args.template):
            with open(args.template, 'r', encoding='utf-8') as file:
                script_template_content = file.read()
        else:
            raise FileNotFoundError(f"模板文件不存在: {args.template}")
    if script_template_content:
        for i, filepath in enumerate(matched_files):
            log_verbose(f"生成第 {i+1}/{len(matched_files)} 个命令")
            command = generate_command(script_template_content, filepath, args.regex)
            commands.append(command)
        if len(commands) > 0:
            print("commands:")
            for i, command in enumerate(commands):
                print(command)
            print("-" * 50)
            print(f"共生成 {len(commands)} 条命令")
    else:
        log_verbose("脚本命令为空，不执行任何操作")
        sys.exit(1)
    if args.join:
        splicing = args.join.join(commands)
        print('splicing:')
        print(splicing)
    # 执行命令
    if args.execute:
        log_verbose(f"开始命令执行, 进度条显示: {'是' if args.progress else '否'}")
        print("execute...")
        try:
            if args.multi_process and len(commands) > 1:
                # 多进程执行
                print(f"使用多进程模式，工作进程数: {min(args.max_workers, len(commands))}")
                log_verbose("注意：多进程模式下详细日志可能会出现乱序")

                with Pool(processes=min(args.max_workers, len(commands))) as pool:
                    results = pool.map(execute_command, commands)

                success_count = sum(results)
                print(f"\n执行完成: {success_count}/{len(commands)} 条命令执行成功")
                log_verbose(f"多进程执行完成，成功率: {success_count}/{len(commands)}")

            else:
                # 单进程顺序执行
                print(f"使用单进程顺序执行，任务数量: {len(commands)}")
                success_count = 0
                # 根据参数决定是否使用进度条
                if args.progress:
                    command_iterator = tqdm.tqdm(commands, desc="执行命令", unit="cmd")
                    log_verbose("启用进度条显示")
                else:
                    command_iterator = commands
                    log_verbose("禁用进度条显示")
                
                for i, command in enumerate(command_iterator):
                    log_verbose(f"执行第 {i+1}/{len(commands)} 个命令")
                    if execute_command(command):
                        success_count += 1
                        log_verbose(f"第 {i+1} 个命令执行成功")
                    else:
                        log_verbose(f"第 {i+1} 个命令执行失败")
                
        except KeyboardInterrupt:
            log_verbose("用户按下 Ctrl+C 中断程序")
            print("\n用户中断执行")
            sys.exit(1)
        except Exception as e:
            log_verbose(f"程序执行过程中发生未预期的异常: {str(e)}")
            log_verbose(f"异常类型: {type(e).__name__}")
            print(f"程序执行出错: {str(e)}")
            sys.exit(1)

if __name__ == "__main__":
    main()
